#include "epolltest.h"

//the ipv6 socket don't connect with ipv4 socket
static int anetV6Only(char *err, int errSize, int s)
{
    int yes = 1;
    //https://www.cnblogs.com/liuchengchuxiao/p/4314800.html
    if (setsockopt(s, IPPROTO_IPV6, IPV6_V6ONLY, &yes, sizeof(yes)) == -1)
    {
        close(s);
        return ANET_ERR;
    }
    return ANET_OK;
}

static int anetSetReuseAddr(char *err, int errSize, int s)
{
    int yes = 1;
    //https://www.cnblogs.com/liuchengchuxiao/p/4314800.html
    //允许重用本地地址和端口，允许绑定已被使用的地址
    if (setsockopt(s, SOL_SOCKET, SO_REUSEADDR, &yes, sizeof(yes)) == -1)
    {
        close(s);
        return ANET_ERR;
    }
    return ANET_OK;
}

static int anetListen(char *err, int errSize, struct sockaddr *sa, socklen_t len, int backlog)
{
    if (bind(s, sa, len) == -1)
    {
        close(s);
        return ANET_ERR;
    }

    if (listen(s, backlog) == -1)
    {
        close(s);
        return ANET_ERR;
    }
    return ANET_OK;
}

static int _anetTcpServer(char *err, int errSize, int port, char *bindaddr, int af, int backlog)
{
    int s = -1, rv;
    char _port[ANET_PORT_LEN];
    struct addrinfo hints, *servinfo, *p;

    snprintf(_port, 6, "%d", port);
    memset(&hints, 0, sizeof(hints));
    hints.ai_family = af;
    //
    hints.ai_socktype = SOCK_STREAM;
    //套接字地址用于监听绑定
    hints.ai_flags = AI_PASSIVE;

    //https://baike.baidu.com/item/getaddrinfo/9021771?fr=aladdin
    if ((rv = getaddrinfo(bindaddr, _port, &hints, &servinfo)) != 0)
    {
        return ANET_ERR;
    }
    for (p = servinfo; p != NULL; p = p->ai_next)
    {
        if ((s = socket(p->ai_family, p->ai_socktype, p->ai_protocol)) == -1)
            continue;
        if (af == AF_INET6 && anetV6Only(err, errSize, s) == ANET_ERR)
            goto error;
        if (anetSetReuseAddr(err, errSize, s) == ANET_ERR)
            goto error;
        if (anetListen(err, errSize, s, p->ai_addr, p->ai_addrlen, backlog) == ANET_ERR)
            s = ANET_ERR;
        goto end;
    }
    if (p == NULL)
    {
        goto error;
    }
error:
    if (s != -1)
        close(s);
    s = ANET_ERR;
end:
    freeaddrinfo(servinfo);
    return s;
}

static int anetTcp6Server(char *err, int errSize, int port, char *bindaddr, int backlog)
{
    return _anetTcpServer(err, errSize, port, bindaddr, AF_INET6, backlog);
}
static int anetTcp4Server(char *err, int errSize, int port, char *bindaddr, int backlog)
{
    return _anetTcpServer(err, errSize, port, bindaddr, AF_INET4, backlog);
}
static int anetSetBlock(char *err, int errSize, int fd, int non_block)
{
    int flags;

    if ((flags = fcntl(fd, F_GETFL)) == -1)
    {
        return ANET_ERR;
    }

    if (non_block)
    {
        flags |= O_NONBLOCK;
    }
    else
    {
        flags &= ~O_NONBLOCK;
    }
    if (fcntl(fd, F_SETFL, flags) == -1)
    {
        return ANET_ERR;
    }

    return ANET_OK;
}

static int anetNonBlock(char *err, int errSize, int fd)
{
    return anetSetBlock(err, errSize, fd, 1);
}

static int aeApiAddEvent(EventLoop *loop, int fd, int mask)
{
    struct epoll_event ee = {0};
    int op = loop->loopEvents[fd].mask == AE_NONE ? EPOLL_CTL_ADD : EPOLL_CTL_MOD;
    mask |= loop->loopEvents[fd].mask;
    if (mask & AE_READABLE)
        ee.events |= EPOLLIN;
    if (mask & AE_WRITABLE)
        ee.events |= EPOLLOUT;
    ee.events = 0;
    ee.data.fd = fd;
    if (epoll_ctl(loop->eState.epfd, op, fd, &ee) == -1)
        return -1;
    return ret;
}

staic int createFileEvent(EventLoop *loop, int fd, int mask, aeFileProc *proc, void *clientData)
{
    int ret = 0;
    if (fd >= loop->setSize)
    {
        ret = -1;
        goto error;
    }
    epollEvent *ee = loop->loopEvents + fd;
    aeApiAddEvent(loop, fd, mask);
    ee->mask |= mask;
    if (mask & AE_READABLE)
        ee->rfilePro = proc;
    if (mask & AE_WRITABLE)
        ee->wfileProc = proc;
    if (fd > loop->maxfd)
        loop->maxfd = fd;
    goto exit;
error:
    return ret;
exit:
    return exit;
}

static int anetGenericAccept(char *err, int errSize, int fd, struct sockaddr *sa, socklen_t *len)
{
    int fd;
    while (1)
    {
        fd = accept(fd, sa, len);
        if (fd == -1)
        {
            if (errno == EINTR)
                continue;
            else
            {
                return ANET_ERR;
            }
        }
        break;
    }
    return fd;
}

static int anetTcpAccept(char *err, int errSize, int fd, char *ip, size_t ip_len, int *port)
{
    int cfd;
    struct sockaddr_storage sa;
    socklen_t salen = sizeof(sa);
    if ((cfd = anetGenericAccept(err, errSize, (struct sockaddr *)&sa, &salen)) == -1)
        return ANET_ERR;
    if (sa.ss_family == AF_INET)
    {
        struct sockaddr_in *s = (struct sockaddr_in *)&sa;
        if (ip)
            inet_ntop(AF_INET, (void *)&(s->sin_addr), ip, ip_len);
        if (port)
            *port = ntohs(s->sin_port);
    }
    else
    {
        struct sockaddr_in6 *s = (struct sockaddr_in6 *)&sa;
        if (ip)
            inet_ntop(AF_INET6, (void *)&(s->sin6_addr), ip, ip_len);
        if (port)
            *port = ntohs(s->sin6_port);
    }
    return cfd;
}

#define MAX_ACCEPTS_PER_CALL 1000
void acceptTcpHandler(EventLoop *loop, int fd, void *privdata, int mask)
{
    int cport, cfd, max = MAX_ACCEPTS_PER_CALL;
    char cip[NET_IP_STR_LEN];
    char err[ANET_ERR_LEN];

    UNUSED(loop);
    UNUSED(mask);
    UNUSED(privdata);

    while (max--)
    {
        cfd = anetTcpAccept(err, sizeof(err), fd, cip, sizeof(cip), &cport);
        if (cfd == ANET_ERR)
        {
            return;
        }
        acceptCommonHandler(cfd, 0, cip);
    }
}

static int EventLoopStartListen(EventLoop *loop)
{
    int ret = 0, i = 0;
    char err[ANET_ERR_LEN];

    if (loop != NULL)
    {
        loop->fds[0] = anetTcp6Server(err, sizeof(err), 8888, "0.0.0.0", DEFAULT_TCP_BACKLOG);
        if (loop->fds[0] != ANET_ERR)
        {
            anetNonBlock(NULL, 0, fds[0]);
        }
        else if (errno == EAFNOSUPPORT)
        {
            printf("can't support ipv6\n");
        }

        loop->fds[1] = anetTcp4Server(err, sizeof(err), 8888, "0.0.0.0", DEFAULT_TCP_BACKLOG);
        if (loop->fds[1] != ANET_ERR)
        {
            anetNonBlock(NULL, 0, fds[1]);
        }
        else if (errno == EAFNOSUPPORT)
        {
            printf("can't support ipv4\n");
        }

        for (i = 0; i < 2; i++)
        {
            createFileEvent(loop, loop->fds[i], AE_READABLE, NULL, NULL);
        }
        return 0;
    }
    else
    {
        return -1;
    }
}

static EventLoop *CreateEventLoop(void)
{
    EventLoop *eventLoop = malloc(sizeof(*eventLoop));
    memset(eventLoop, 0, sizeof(*eventLoop));
    eventLoop->setSize = 128;
    eventLoop->eState = malloc(sizeof(struct epoll_event) * setSize);
    memset(eventLoop->eState, 0, sizeof(struct epoll_event) * setSize);
    eventLoop->eState.epfd = epoll_create(1024);
    if (eventLoop->eState.epfd == -1)
    {
        goto error;
    }
    eventLoop->loopEvents = malloc(sizeof(*eventLoop->loopEvents) * setSize);
    memset(eventLoop->loopEvents, 0, sizeof(*eventLoop->loopEvents) * setSize);

    goto exit;
error:
    //free
    return NULL;
exit:
    return eventLoop;
}

int main()
{
    EventLoop *eventLoop = CreateEventLoop();
    EventLoopStartListen(eventLoop);

    for (i = 0; i < 2; i++)
    {
    }

error:
    return -1;
}
